﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using FarseerGames.FarseerPhysics;
using FarseerGames.FarseerPhysics.Dynamics;
using FarseerGames.FarseerPhysics.Collisions;
using FarseerGames.FarseerPhysics.Factories;
using Microsoft.Xna.Framework.Content.Pipeline.Serialization.Compiler;
using System.ComponentModel;
using System.Diagnostics;

namespace FolderData
{

    public enum OnCollisionEvent
    {
        None,
        ToCheckPoint,
        StageEnd,
    }

    public class Item : Substance
    {

        public class ItemDataSet : Substance.DataSet
        {
            protected ItemKind _kind;
            public ItemKind Kind
            {
                get { return _kind; }
                set { _kind = value; }
            }
            public override void Serialize(ContentWriter output)
            {
                base.Serialize(output);
                output.Write((int)Kind);
            }
            public override void Deserialize(ContentReader input)
            {
                base.Deserialize(input);
                this.Kind = (ItemKind)input.ReadInt32();
            }
        }

        public enum ItemKind
        {
            KEY     = 0
        }

        #region Fields
        protected ItemKind _kind;
        protected string _spriteAssetName;
        protected Texture2D _sprite;
        protected readonly int KeyAnimationDuration;    //열쇠의 애니메이션 총 지속 시간
        protected int _elapsedTime;
        protected List<NextStateCallBack> _nextCallbacks;


        [ReadOnly(true)]
        public ItemKind Kind
        {
            get { return _kind; }
            set 
            { 
                _kind = value;
                if (_kind == ItemKind.KEY)
                    _state = SubstanceState.VALIDATED;  //열쇠의 기본 Validation 상태는 Valid이다.
            }
        }


        [Browsable(false)]
        public string SpriteAssetName
        {
            get { return _spriteAssetName; }
            set { _spriteAssetName = value; }
        }

        public override Texture2D Texture
        {
            get { return _sprite; }
        }
        public override Rectangle? Rect
        {
            get { return null; }
        }
        public override bool Activation
        {
            get
            {
                return base.Activation;
            }
            set
            {
                if (value)
                    _nextCallbacks[(int)this.Kind](SubstanceState.VALIDATED);
                else
                    _nextCallbacks[(int)this.Kind](SubstanceState.INVALIDATED);
            }
        }

        #endregion

        public override void BindSimulator(PhysicsSimulator simulator)
        {
            base.BindSimulator(simulator);
            makeBodyGeom(Mass, Verts, out _body, out _geom, out _origin);
            simulator.Add(_body);
            simulator.Add(_geom);
        }

        public override void UnbindSimulator()
        {
            base.UnbindSimulator();
            _simulator.Remove(_body);
            _simulator.Remove(_geom);
        }

        public override void Update(GameTime gameTime)
        {
            if (Kind == ItemKind.KEY)
            {
                if (_state == SubstanceState.VALIDATING || _state == SubstanceState.INVALIDATING)
                {
                    _elapsedTime += gameTime.ElapsedGameTime.Milliseconds;
                    _nextCallbacks[(int)this.Kind](_state == SubstanceState.VALIDATING ? SubstanceState.VALIDATED : SubstanceState.INVALIDATED);
                }
            }
        }

        public override object Clone()
        {
            Item it = new Item();
            Substance._clone(this as Substance, it as Substance);
            it.SpriteAssetName = this.SpriteAssetName;
            it.Kind = this.Kind;
            it.SetTexture(this.Texture);
            return it as object;
        }
        
        public void SetDatas(ItemDataSet ds)
        {
            base.SetDatas(ds as Substance.DataSet);
            this.Kind = ds.Kind;
            if (ds.IsVirtualGeom)
            {
                _simulator.Remove(_body);
                _simulator.Remove(_geom);
            }
        }

        public ItemDataSet GetDatas()
        {
            ItemDataSet ds = new ItemDataSet();
            _getDatas(ds);
            ds.Kind = this.Kind;
            return ds;
        }

        public void SetTexture(Texture2D sprite)
        {
            _sprite = sprite;
        }

        #region NextCallbacks
        protected void nextKeyState(SubstanceState state)
        {
            Debug.Assert(Kind == ItemKind.KEY);
            if (state == SubstanceState.INVALIDATED)
            {
                if (_state != SubstanceState.INVALIDATING)
                {
                    _state = SubstanceState.INVALIDATING;
                    _elapsedTime = 0;
                }
                _colorMod = new Color(1f, 1f, 1f, (KeyAnimationDuration - (float)_elapsedTime) / KeyAnimationDuration);
                if (_elapsedTime >= KeyAnimationDuration)
                {
                    _state = SubstanceState.INVALIDATED;
                    _elapsedTime = 0;
                    _colorMod = new Color(0f, 0f, 0f, 0f);
                    _geom.CollisionEnabled = false;
                    _geom.CollidesWith = CollisionCategory.None;
                }
            }
            else if (state == SubstanceState.VALIDATED)
            {
                if (_state != SubstanceState.VALIDATING)
                {
                    _state = SubstanceState.VALIDATING;
                    _elapsedTime = 0;
                }
                _colorMod = new Color(1, 1, 1, ((float)_elapsedTime) / KeyAnimationDuration);
                if (_elapsedTime >= KeyAnimationDuration)
                {
                    _state = SubstanceState.VALIDATED;
                    _elapsedTime = 0;
                    _colorMod = Color.White;
                    _geom.CollisionEnabled = true;
                }
            }
            else
                throw new ArgumentException("State가 Invalidated 또는 Validated가 아닙니다.");
        }
        #endregion

        public Item()
            : base()
        {
            _nextCallbacks = new List<NextStateCallBack>();
            _nextCallbacks.Add(new NextStateCallBack(nextKeyState));
        }

        #region Processor
        public class ItemReader : ContentTypeReader<Item>
        {
            protected override Item Read(ContentReader input, Item existingInstance)
            {
                Item it = new Item();
                it.Name = input.ReadString();
                int count = input.ReadInt32();
                it.Verts = new Vertices();
                for (int i = 0; i < count; ++i)
                    it.Verts.Add(new Vector2(input.ReadSingle(), input.ReadSingle()));
                count = input.ReadInt32();
                it.TextureVerts = new Vertices();
                for (int i = 0; i < count; ++i)
                    it.TextureVerts.Add(new Vector2(input.ReadSingle(), input.ReadSingle()));
                it.Mass = input.ReadSingle();
                it.Layer = (LayerOrder)input.ReadInt32();

                it._property = (Properties)input.ReadUInt64();

                it.Kind = (ItemKind)input.ReadInt32();
                it.SpriteAssetName = input.ReadString();
                it.SetTexture(input.ContentManager.Load<Texture2D>(it.SpriteAssetName));

                it.OnCollision = (OnCollisionEvent)input.ReadInt32();
                return it;
            }
        }
        
        [ContentTypeWriter]
        public class ItemWriter : ContentTypeWriter<Item>
        {
            protected override void Write(ContentWriter output, Item value)
            {
                output.Write(value.Name);
                output.Write(value.Verts.Count);
                for (int i = 0; i < value.Verts.Count; ++i)
                {
                    output.Write(value.Verts[i].X);
                    output.Write(value.Verts[i].Y);
                }
                output.Write(value.TextureVerts.Count);
                for (int i = 0; i < value.TextureVerts.Count; ++i)
                {
                    output.Write(value.TextureVerts[i].X);
                    output.Write(value.TextureVerts[i].Y);
                }
                output.Write(value.Mass);
                output.Write((int)value.Layer);
                output.Write((UInt64)value._property);
                output.Write((int)value.Kind);
                output.Write(value.SpriteAssetName);

                output.Write((int)value.OnCollision);
            }
            public override string GetRuntimeReader(TargetPlatform targetPlatform)
            {
                return typeof(Item.ItemReader).AssemblyQualifiedName;
            }
            public override string GetRuntimeType(TargetPlatform targetPlatform)
            {
                return typeof(Item).AssemblyQualifiedName;
            }
        }
        #endregion
    }
}
